package com.sz.netty.util;

import com.sz.netty.entity.DtuFtpMessageLog;
import com.sz.netty.entity.DtuFtpMessageDetailSim;
import com.sz.netty.entity.DtuFtpMessageDetail;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.List;

/**
 * 协议数据解析器
 */
@Slf4j
public class ProtocolDataParser {
    
    // 协议常量
    private static final byte[] HEADER = {0x7b, 0x7b, 0x7b};
    private static final byte TYPE_CODE = (byte) 0x88;
    private static final byte[] TRAILER = {0x7d, 0x7d, 0x7d};
    private static final byte[] END_MARKER = {0x0d, 0x0a};
    private static final int DEVICE_ID_LENGTH = 6;
    private static final int PACKET_LENGTH_FIELD_LENGTH = 2;
    private static final int CHECKSUM_LENGTH = 1;
    
    /**
     * 解析结果类
     */
    @Data
    public static class ParseResult {
        private DtuFtpMessageLog messageLog;
        private DtuFtpMessageDetailSim simDetail;
        private List<DtuFtpMessageDetail> gpsDetails = new ArrayList<>();
    }
    
    /**
     * 解析数据包
     * @param packet 数据包
     * @return 解析结果
     */
    public static ParseResult parsePacket(byte[] packet) {
        ParseResult result = new ParseResult();
        
        try {
            // 提取设备ID
            byte[] deviceId = Arrays.copyOfRange(packet, HEADER.length + 1, HEADER.length + 1 + DEVICE_ID_LENGTH);
            String dtuNumber = new String(deviceId, StandardCharsets.US_ASCII).trim();
            
            // 提取内容
            byte[] content = extractContent(packet);
            
            // 提取命令字
            int commandType = content.length > 0 ? content[0] & 0xFF : 0;
            
            // 创建消息日志实体
            DtuFtpMessageLog messageLog = new DtuFtpMessageLog();
            messageLog.setReceiveMessage(packet);
            messageLog.setReceiveMessageType(commandType);
            messageLog.setDtuNumber(dtuNumber);
            
            result.setMessageLog(messageLog);
            
            // 根据命令字解析详细内容
            if (commandType == 0x01 && content.length >= 4) {
                // 解析SIM卡信息（心跳/握手命令）
                parseSimCardInfo(result, content);
            } else if (commandType == 0x02 && content.length >= 25) {
                // 解析GPS信息
                parseGpsInfo(result, content, packet);
            }
        } catch (Exception e) {
            log.error("解析数据包时发生错误: {}", e.getMessage(), e);
        }
        
        return result;
    }
    
    /**
     * 提取内容字段
     * @param packet 数据包
     * @return 内容字段
     */
    private static byte[] extractContent(byte[] packet) {
        // Validate packet length first
        if (packet == null || packet.length < HEADER.length + 1 + DEVICE_ID_LENGTH + PACKET_LENGTH_FIELD_LENGTH + TRAILER.length + CHECKSUM_LENGTH) {
            return new byte[0]; // Return empty array for invalid packets
        }
        
        int contentStart = HEADER.length + 1 + DEVICE_ID_LENGTH + PACKET_LENGTH_FIELD_LENGTH;
        int checksumIndex = packet.length - TRAILER.length - CHECKSUM_LENGTH;
        
        // Check if packet ends with end markers and adjust checksum index
        if (packet.length >= 2) {
            // Check for 0x0D, 0x0A end marker
            if (packet[packet.length - 2] == 0x0D && packet[packet.length - 1] == 0x0A) {
                checksumIndex = packet.length - 2 - TRAILER.length - CHECKSUM_LENGTH;
            }
            // We ignore packets ending with 0x7B, 0x7B
        }
        
        // Ensure contentStart doesn't exceed checksumIndex and both are valid
        if (contentStart < 0 || checksumIndex < 0 || contentStart >= checksumIndex || contentStart >= packet.length || checksumIndex > packet.length) {
            return new byte[0]; // Return empty array if no content or invalid indices
        }
        
        return Arrays.copyOfRange(packet, contentStart, checksumIndex);
    }
    
    /**
     * 解析SIM卡信息（命令字0x01）
     * @param result 解析结果
     * @param content 内容字段
     */
    private static void parseSimCardInfo(ParseResult result, byte[] content) {
        try {
            // 根据协议，SIM卡信息格式为：
            // 第1字节：指令字（0x01）
            // 第2字节：信号强度
            // 第3字节：SIM卡编号长度
            // 第4字节开始：SIM卡编号内容（长度由第3字节指定）
            
            if (content.length < 4) {
                log.warn("SIM卡信息数据长度不足");
                return;
            }
            
            // 信号强度（第2字节）
            int signalLevel = content[1] & 0xFF;
            
            // SIM卡编号长度（第3字节）
            int simNumberLength = content[2] & 0xFF;
            
            // 检查数据长度是否足够
            if (content.length < 3 + simNumberLength) {
                log.warn("SIM卡编号数据长度不足: expected at least {}, got {}", 
                         3 + simNumberLength, content.length);
                return;
            }
            
            // SIM卡编号内容（从第4字节开始，长度为simNumberLength）
            byte[] simNumberBytes = Arrays.copyOfRange(content, 3, 3 + simNumberLength);
            String simNumber = new String(simNumberBytes, StandardCharsets.US_ASCII).trim();
            
            // 创建SIM卡详情实体
            DtuFtpMessageDetailSim simDetail = new DtuFtpMessageDetailSim();
            simDetail.setSimNumber(simNumber);
            simDetail.setSignalLevel(signalLevel);
            
            result.setSimDetail(simDetail);
            log.debug("解析到SIM卡信息: 信号强度={}, SIM卡号={}", signalLevel, simNumber);
        } catch (Exception e) {
            log.error("解析SIM卡信息时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 解析GPS信息（命令字0x02）
     * @param result 解析结果
     * @param content 内容字段
     * @param packet 完整数据包
     */
    private static void parseGpsInfo(ParseResult result, byte[] content, byte[] packet) {
        try {
            // 数据记录条数（第3字节）
            int dataCount = content[2] & 0xFF;
            
            // 解析每条数据
            for (int i = 0; i < dataCount; i++) {
                // 每条记录从第4字节开始，每条记录长度根据协议分析确定
                // 根据协议分析，每条记录包含：
                // 6字节时间 + 1字节随机时间序列 + 1字节状态位 + 2字节雷达距离 +
                // 11字节经度 + 10字节纬度 + 2字节速度 + 2字节方向 + 1字节卫星数量 + 4字节HDOP
                int recordLength = 40; // 6+1+1+2+11+10+2+2+1+4 = 40 bytes
                int offset = 3 + (i * recordLength); // 每条记录40字节
                if (offset + recordLength <= content.length) {
                    DtuFtpMessageDetail gpsDetail = parseSingleGpsData(content, offset);
                    if (gpsDetail != null) {
                        gpsDetail.setMessageNum(dataCount);
                        // 设置完整报文内容
                        gpsDetail.setMessageContent(packet);
                        // 设置单条记录数据
                        byte[] singleRecord = Arrays.copyOfRange(content, offset, offset + recordLength);
                        gpsDetail.setMessage(singleRecord);
                        result.getGpsDetails().add(gpsDetail);
                    }
                }
            }
        } catch (Exception e) {
            log.error("解析GPS信息时发生错误: {}", e.getMessage(), e);
        }
    }
    
    /**
     * 解析单条GPS数据
     * @param content 内容字段
     * @param offset 偏移量
     * @return GPS详情实体
     */
    private static DtuFtpMessageDetail parseSingleGpsData(byte[] content, int offset) {
        try {
            DtuFtpMessageDetail gpsDetail = new DtuFtpMessageDetail();
            
            // 时间信息（6字节，十六进制存储）
            byte[] timeBytes = Arrays.copyOfRange(content, offset, offset + 6);
            // 解析6字节时间戳
            Date parsedTime = parseHexTimestamp(timeBytes);
            gpsDetail.setTime(parsedTime);
            
            // Log the timestamp bytes for debugging
            log.debug("Parsing timestamp from bytes: {} {} {} {} {} {}", 
                String.format("%02X", timeBytes[0]), 
                String.format("%02X", timeBytes[1]), 
                String.format("%02X", timeBytes[2]), 
                String.format("%02X", timeBytes[3]), 
                String.format("%02X", timeBytes[4]), 
                String.format("%02X", timeBytes[5]));
            log.debug("Parsed timestamp: {}", parsedTime);
            
            // 随机时间序列（1字节）
            byte randTimeByte = content[offset + 6];
            gpsDetail.setRandTimeSort(String.format("%02X", randTimeByte));
            
            // 状态位（1字节）
            int statusBit = content[offset + 7] & 0xFF;
            gpsDetail.setStatusBit(String.format("%02X", statusBit));
            
            // 解析状态位的各个字段
            // Bit0：0 - 无定位，1 - 已定位（无定位时 Bit1、Bit2 无意义）
            // Bit1：0 - 北纬，1 - 南纬
            // Bit2：0 - 东经，1 - 西经
            // Bit3：0 - 振动开关闭合，1 - 振动开关断开
            // Bit4：0 - 报警器断电，1 - 报警器通电
            gpsDetail.setPositioningStatus((statusBit & 0x01) != 0);
            gpsDetail.setAlarmPowerStatus((statusBit & 0x10) != 0); // Bit4
            gpsDetail.setVibrationStatus((statusBit & 0x08) != 0); // Bit3
            
            // 雷达距离（2字节，大端序）
            int radarDistance = ((content[offset + 8] & 0xFF) << 8) | (content[offset + 9] & 0xFF);
            gpsDetail.setRadarDistance((double) radarDistance);
            
            // 经度（11字节，ASCII字符串）
            byte[] longitudeBytes = Arrays.copyOfRange(content, offset + 10, offset + 21);
            String longitudeStr = new String(longitudeBytes, StandardCharsets.US_ASCII).trim();
            try {
                double longitudeValue = Double.parseDouble(longitudeStr);
                // Convert from degree-minute format to decimal degrees
                BigDecimal longitudeDecimal = BigDecimal.valueOf(convertDegreeMinuteToDecimal(longitudeValue));
                gpsDetail.setLongitude(longitudeDecimal);
            } catch (NumberFormatException e) {
                log.warn("经度解析失败: {}", longitudeStr);
                gpsDetail.setLongitude(BigDecimal.ZERO);
            }
            
            // 纬度（10字节，ASCII字符串）
            byte[] latitudeBytes = Arrays.copyOfRange(content, offset + 21, offset + 31);
            String latitudeStr = new String(latitudeBytes, StandardCharsets.US_ASCII).trim();
            try {
                double latitudeValue = Double.parseDouble(latitudeStr);
                // Convert from degree-minute format to decimal degrees
                BigDecimal latitudeDecimal = BigDecimal.valueOf(convertDegreeMinuteToDecimal(latitudeValue));
                gpsDetail.setLatitude(latitudeDecimal);
            } catch (NumberFormatException e) {
                log.warn("纬度解析失败: {}", latitudeStr);
                gpsDetail.setLatitude(BigDecimal.ZERO);
            }
            
            // 速度（2字节，BCD码）
            int speedHigh = content[offset + 31] & 0xFF;
            int speedLow = content[offset + 32] & 0xFF;
            int speed = (parseBcd((byte) speedHigh) * 100) + parseBcd((byte) speedLow);
            gpsDetail.setSpeed(BigDecimal.valueOf(speed / 10.0)); // Convert to km/h (0.1km/h unit)
            
            // 方向（2字节，BCD码）
            int directionHigh = content[offset + 33] & 0xFF;
            int directionLow = content[offset + 34] & 0xFF;
            int direction = (parseBcd((byte) directionHigh) * 100) + parseBcd((byte) directionLow);
            gpsDetail.setDirection(direction);
            
            // 卫星数量（1字节，BCD码）
            int satelliteNum = parseBcd(content[offset + 35]);
            gpsDetail.setSatelliteNum(satelliteNum);
            
            // 水平精度因子（4字节，ASCII字符串）
            byte[] hdopBytes = Arrays.copyOfRange(content, offset + 36, offset + 40);
            String hdopStr = new String(hdopBytes, StandardCharsets.US_ASCII).trim();
            try {
                double hdopValue = Double.parseDouble(hdopStr);
                // Round to 2 decimal places
                hdopValue = Math.round(hdopValue * 100.0) / 100.0;
                gpsDetail.setHorizontalAccuracyFactor(hdopValue);
            } catch (Exception e) {
                log.warn("水平精度因子解析失败: {}", hdopStr);
                gpsDetail.setHorizontalAccuracyFactor(0.0);
            }
            
            return gpsDetail;
        } catch (Exception e) {
            log.error("解析单条GPS数据时发生错误: {}", e.getMessage(), e);
            return null;
        }
    }
    
    /**
     * 解析十六进制编码的时间戳
     * 格式：YY MM DD HH MM SS (6字节)
     * @param timeBytes 6字节时间数据
     * @return 解析后的Date对象
     */
    private static Date parseHexTimestamp(byte[] timeBytes) {
        try {
            // 解析十六进制
            int year = timeBytes[0] & 0xFF; // 年份后两位
            int month = timeBytes[1] & 0xFF; // 月份
            int day = timeBytes[2] & 0xFF;   // 日
            int hour = timeBytes[3] & 0xFF;  // 小时
            int minute = timeBytes[4] & 0xFF; // 分钟
            int second = timeBytes[5] & 0xFF; // 秒
            
            // 转换为完整年份（假设20xx年）
            year += 2000;
            
            // 构造日期时间 - the device sends timestamps in local time (Asia/Shanghai)
            // Since the database is configured with serverTimezone=Asia/Shanghai,
            // we need to treat the parsed time as already being in Asia/Shanghai timezone
            LocalDateTime localDateTime = LocalDateTime.of(year, month, day, hour, minute, second);
            
            // Convert to Instant using Asia/Shanghai timezone to avoid double conversion
            // The JDBC driver will not apply additional timezone conversion if we do this correctly
            java.time.ZoneId shanghaiZone = java.time.ZoneId.of("Asia/Shanghai");
            Date date = Date.from(localDateTime.atZone(shanghaiZone).toInstant());
            
            return date;
        } catch (Exception e) {
            log.warn("解析十六进制时间戳失败，使用零时间: {}", e.getMessage());
            return new Date(0); // 出错时返回零时间（Unix epoch）作为后备
        }
    }
    
    /**
     * 将度分格式转换为十进制度格式
     * 例如：11431.74179 (114度31.74179分) 转换为 114.52902983333333度
     * @param degreeMinute 度分格式的值
     * @return 十进制度格式的值
     */
    private static double convertDegreeMinuteToDecimal(double degreeMinute) {
        // 获取度数部分（整数部分除以100取整）
        int degrees = (int) (degreeMinute / 100);
        // 获取分数部分（原值减去度数部分乘以100）
        double minutes = degreeMinute - (degrees * 100);
        // 转换为十进制度：度数 + 分数/60
        return degrees + (minutes / 60.0);
    }
    
    /**
     * 解析BCD码
     * @param b BCD字节
     * @return 解析后的十进制值
     */
    private static int parseBcd(byte b) {
        int high = (b & 0xF0) >> 4;
        int low = b & 0x0F;
        return high * 10 + low;
    }
    
    /**
     * 将字节数组转换为十六进制字符串
     * @param bytes 字节数组
     * @return 十六进制字符串
     */
    private static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X", b));
        }
        return sb.toString();
    }
}